// Copyright(c) Sonosemi Medical Ltd. Embedded Software Team.
// Author: Zhang Yisong(zhangyisong@sonosemi.com)

#pragma once

#include "cib/cib_connect.hpp"
#include "etl/scheduler.h"
#include "etl/singleton.h"
#include "etl/task.h"
#include "internal/active_concepts.hpp"
#include "internal/active_service.hpp"
#include "internal/concepts/active_task_config.hpp"
#include <cstdio>
namespace active {

struct TaskVoid {};

namespace internal {

template <typename T>
static constexpr auto is_task_void = std::is_same_v<TaskVoid, T>;
class SchedulerPolicy {
public:
  bool schedule_tasks(etl::ivector<etl::task *> &task_list) {
    bool idle = true;
    for (auto task : task_list) {
      if (task->get_task_priority() > 0) {
        while (task->task_request_work() > 0) {
          task->task_process_work();
          idle = false;
        }
      } else {
        if (task->task_request_work() > 0) {
          task->task_process_work();
          idle = false;
        }
      }
    }
    return idle;
  }
};

} // namespace internal
inline static auto ProcessQueuedEvents() {
  cib::CallService<active::service::ProcessAllQueuedEvents>();
}
template <typename TActive, size_t kQueueSize, size_t kPriority>
struct TaskDef final : public etl::task {
  using EventQueue = typename TActive::EventQueueType;
  TaskDef() : task(kPriority) {}
  auto task_request_work() const -> uint32_t override {
    return TActive::QueueSize();
  }
  auto task_process_work() -> void override { TActive::ProcessingWork(); }
};

template <size_t kTaskCount>
class TaskScheduler final
    : public etl::scheduler<internal::SchedulerPolicy, kTaskCount> {
public:
  using Base = etl::scheduler<internal::SchedulerPolicy, kTaskCount>;
  TaskScheduler() : m_idle_cb(*this) { this->set_idle_callback(m_idle_cb); }
  auto start() -> void override {
    Base::scheduler_exit = false;
    Base::start();
  }
  auto Exit() -> void { this->exit_scheduler(); }

private:
  etl::function_mv<TaskScheduler, &TaskScheduler::Exit> m_idle_cb;
};

namespace internal {
template <typename TTask> static consteval auto CountNonVoidTask() -> int {
  if constexpr (not internal::is_task_void<TTask>) {
    return 1;
  } else {
    return 0;
  }
}

template <typename TTask, typename TSch> static auto AddTask(TSch &sch) {
  if constexpr (not is_task_void<TTask>) {
    etl::singleton<TTask>::create();
    sch.add_task(etl::singleton<TTask>::instance());
  }
}

} // namespace internal
/// Make a scheduler reference.
///@note the sheduler reference should be called by its method
/// sheduler_obj.start() in a thread if using RTOS or the main function in a
/// bare metal applicaiton
template <typename... TTask> static auto MakeScheduler() {
  constexpr size_t kTaskCount = ((internal::CountNonVoidTask<TTask>()) + ...);
  if constexpr (kTaskCount > 0) {
    using SchedulerSinleton = etl::singleton<TaskScheduler<kTaskCount>>;
    SchedulerSinleton::create();
    auto &sch = SchedulerSinleton::instance();
    (internal::AddTask<TTask>(sch), ...);
    active::service::ProcessAllQueuedEvents::Connect(
        []() { SchedulerSinleton::instance().start(); });
  }
}

template <size_t kSize>
struct QueueSize : public std::integral_constant<size_t, kSize> {};
template <size_t kSize>
struct Priority : public std::integral_constant<size_t, kSize> {};

template <typename TQueueSize, typename TPriority = Priority<0>>
constexpr static concepts::TaskConfigable auto
Queued(TQueueSize, TPriority prio = Priority<0>{}) {
  static_assert(
      std::conjunction_v<std::is_same<TQueueSize, QueueSize<TQueueSize::value>>,
                         std::is_same<TPriority, Priority<TPriority::value>>>,
      "It should be initialized as qsize<int>, priority<int>");
  return internal::TaskConfig<TQueueSize::value, TPriority::value>{};
}

template <size_t size> constexpr static auto qsize = QueueSize<size>{};
template <size_t size> constexpr static auto prio = Priority<size>{};

template <size_t kSize, size_t kPrio = 0>
constexpr auto queue = Queued(qsize<kSize>, prio<kPrio>);

template <char... TDigits> consteval auto operator""_prio() {
  constexpr auto kMax = sizeof...(TDigits);
  static_assert(sizeof...(TDigits) < 3,
                "Max priority number must be within 2 digits");
  constexpr auto digits = std::make_tuple(TDigits...);
  if constexpr (kMax == 2) {
    constexpr auto result =
        (std::get<0>(digits) - '0') * 10 + (std::get<0>(digits) - '0');
    return prio<result>;
  } else {
    return prio<std::get<0>(digits) - '0'>;
  }
}

template <char... TDigits> consteval auto operator""_size() {
  constexpr auto kMax = sizeof...(TDigits);
  static_assert(sizeof...(TDigits) < 3,
                "Max priority number must be within 2 digits");
  constexpr auto digits = std::make_tuple(TDigits...);
  if constexpr (kMax == 2) {
    constexpr auto result =
        (std::get<0>(digits) - '0') * 10 + (std::get<1>(digits) - '0');
    return qsize<result>;
  } else {
    return qsize<std::get<0>(digits) - '0'>;
  }
}

} // namespace active